package org.ntu.rtsearch.store;

import java.io.IOException;

/**
 * @author fuhao(email: hfhimage@gmail.com)         
 * @date 2011-7-21
 */
public abstract class OutputStream {
	public static final int BUFFER_SIZE = 16384;
	
	private final byte[] buffer = new byte[BUFFER_SIZE];
	
	// position in file of buffer, used for get the file pointer
	private long bufferStart = 0;
	
	// position in buffer
	private int bufferPos = 0;
	
	/**
	 * write a single byte
	 * @param b
	 * @throws IOException 
	 */
	public final void writeByte(byte b) throws IOException {
		if(bufferPos >= BUFFER_SIZE) {
			flush();
		}
		buffer[bufferPos++] = b;
	}
	
	/**
	 * write a array of bytes
	 * @param b
	 * @param len
	 * @throws IOException 
	 */
	public final void writeBytes(byte[] b, int len) throws IOException {
		for (int i = 0; i < len; i++) {
			writeByte(b[i]);
		}
	}
	
	public final void writeInt(int i) throws IOException {
		for (int j = 24; j >= 0; j -= 8) {
			writeByte((byte)(i >> j));
		}
	}
	
	public final void writeVInt(int i) throws IOException {
		while(i > 127) {
			writeByte((byte)(i & 0x7f | 0x80));
			i >>>= 7;
		}
		writeByte((byte)i);
	}
	
	public final void writeLong(long i) throws IOException {
		writeInt((int)(i >> 32));
		writeInt((int)i);
	}

	
	public final void writeVlong(long i) throws IOException {
		while(i > 127) {
			writeByte((byte) ((i & 0x7f) | 0x80));
			i >>>= 7;
		}
		writeByte((byte)i);
	}
	
	/**
	 * write a string in utf-8
	 * @param str
	 * @throws IOException 
	 */
	public final void writeString(String str) throws IOException {
		writeVInt(str.length());
		writeChars(str, 0, str.length());
	}
	
	/**
	 * write a string as utf-8 encoded chars
	 * @param str
	 * @param i
	 * @param length
	 * @throws IOException 
	 */
	private void writeChars(String s, int start, int len) throws IOException {
		final int end = start + len;
		for(int i = 0; i < end; i++) {
			final int code = (int)s.charAt(i);
			if (code >= 0x01 && code < 0x7F) {
				writeByte((byte) code);
			} else if(((code >= 0x80) && (code < 0x7FF)) || code == 0){
				writeByte((byte) (0xC0 | (code >> 6)));
				writeByte((byte) (0x80 | (code & 0x3F)));
			} else {
				writeByte((byte) (0xE0 | (code >>> 12)));
				writeByte((byte) (0x80 | ((code >> 6) & 0x3F)));
				writeByte((byte) (0x80 | (code & 0x3F)));
			}
		}
	}
	
	public final void flush() throws IOException {
		flushBuffer(buffer, 0, bufferPos);
		bufferStart += bufferPos;
		bufferPos = 0;
	}
		
	public void close() throws IOException {
		flush();
	} 
	
	public void seek(long pos) throws IOException {
		flush();
		bufferStart = pos;
	}
	
	/**
	 * get the pointer in file
	 * @return
	 */
	public final long getFilePointer() {
		return bufferStart + bufferPos;
	}
	
	protected abstract void flushBuffer(byte[] b, int offset, int len) throws IOException;
	
	protected abstract long length() throws IOException;
}
